package nus.iss.laps;

import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import javax.servlet.http.HttpSession;

import nus.iss.laps.cri.LeaveCri;
import nus.iss.laps.dao.EmployeeDAO;
import nus.iss.laps.dao.HolidayDAO;
import nus.iss.laps.dao.LeaveDAO;
import nus.iss.laps.dao.LeavesentitlementDAO;
import nus.iss.laps.general.LapsUtil;
import nus.iss.laps.general.LeaveType;
import nus.iss.laps.model.Employee;
import nus.iss.laps.model.Holiday;
import nus.iss.laps.model.Leave;
import nus.iss.laps.model.Leavesentitlement;
import nus.iss.laps.model.User;
import nus.iss.laps.general.LeaveStatus;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.WebRequest;


@Controller
public class LeaveController {

	@Autowired
	LeaveDAO leaveDAO;
	
	@Autowired
	EmployeeDAO employeeDAO;
	
	@Autowired
	HolidayDAO holidayDAO;
	
	@Autowired
	LeavesentitlementDAO leavesentitlementDAO;
	
	private int getDays(String startDate, String endDate, String leaveType) throws Exception
	{
		Date l_StartDate = LapsUtil.stringToDate(startDate);
		Date l_EndDate = LapsUtil.stringToDate(endDate);
		
		int l_holidays = holidayDAO.getDays(startDate, endDate);
		long l_diff = l_EndDate.getTime() - l_StartDate.getTime();
		long l_duration = (l_diff / (24 * 60 * 60 * 1000)) + 1;
		long l_leaveDuration = 0;
		
		if(leaveType.equals(LeaveType.annual))
		{
			if(l_duration <= 14)
			{
				int l_weekends = 0;
				Calendar l_cal = Calendar.getInstance();
				l_cal.setTime(l_StartDate);
				while (l_StartDate.compareTo(l_EndDate) != 0) {
					if(l_cal.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY ||
							l_cal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY)
					{
						
						l_weekends++;
					}
					l_cal.add(Calendar.DAY_OF_MONTH, 1);
					l_StartDate = l_cal.getTime();
				}
				
				l_leaveDuration = l_duration - (l_holidays + l_weekends);
			}
			else
				l_leaveDuration = l_duration;
		}
		else
			l_leaveDuration = l_duration;
		
		return (int)l_leaveDuration;
	}
	
	private String getMonthHolidayJson(String date) throws SQLException
	{
		String l_json = "";
		String[] l_strCurArr = date.split("/");
		List<Holiday> l_holidayList = holidayDAO.getHolidaysByMonth(l_strCurArr[2] + l_strCurArr[1]);
		
		for (Holiday holiday : l_holidayList) {
			String[] l_strArr = LapsUtil.stringToFormattedDate(holiday.getHoliday()).split("/");
			String l_jsonDate = "{title: '" + holiday.getDescription() + "'" + 
					", start: '" + l_strArr[2] + "-" + l_strArr[1] + "-" + l_strArr[0] + "'" +
					", end: '" + l_strArr[2] + "-" + l_strArr[1] + "-" + l_strArr[0] + "'}";
			l_json += l_jsonDate + "::";
		}
		if(l_holidayList.size() > 0)
			l_json = l_json.substring(0, l_json.length() - 2);
		
		return l_json;
	}
	
	private String getMonthLeavesJson(String date, String empID) throws SQLException
	{
		String l_json = "";
		String[] l_strCurArr = date.split("/");
		List<Leave> l_leaveList = leaveDAO.getLeavesByDate(l_strCurArr[2] + l_strCurArr[1], empID);
		
		for (Leave l_leave : l_leaveList) {
			String[] l_strArr = LapsUtil.stringToFormattedDate(l_leave.getStartDate()).split("/");
			String[] l_strArr1 = LapsUtil.stringToFormattedDate(l_leave.getEndDate()).split("/");
			String l_jsonDate = "{title: '" + l_leave.getReason() + "'" + 
					", start: '" + l_strArr[2] + "-" + l_strArr[1] + "-" + l_strArr[0] + "'" +
					", end: '" + l_strArr1[2] + "-" + l_strArr1[1] + "-" + l_strArr1[0] + "'}";
			l_json += l_jsonDate + "::";
		}
		if(l_leaveList.size() > 0)
			l_json = l_json.substring(0, l_json.length() - 2);
		
		return l_json;
	}
	
	private String validateLeave(String startDate, String empID, Model model, String leaveCat) throws SQLException
	{
		if(!leaveDAO.isApprovedDate(startDate, empID))
		{
			Employee l_emp = employeeDAO.getEmployee(empID);
			int l_totalLeaves = leaveDAO.getLeavesCountByDate(startDate.substring(0, 4) , empID, leaveCat);
			int l_leaveGet = 0;
			Leavesentitlement l_entitlement = leavesentitlementDAO.getLeavesentitlement(l_emp.getLeaveEntitlementID());
			if(leaveCat.equals(LeaveType.annual))
				l_leaveGet = l_entitlement.getAnnualLeave();
			else if(leaveCat.equals(LeaveType.medical))
				l_leaveGet = l_entitlement.getMedicalLeave();
			else
				l_leaveGet = l_entitlement.getCompensationLeave();
				
			if(l_totalLeaves > l_leaveGet)
			{
				model.addAttribute("message", "Cannot take more leave. Your leave limit is exceeded.");
				return "invalid";
			}
		}
		else
		{
			model.addAttribute("message", "You already have a leave on that date.");
			return "invalid";
		}
		
		
		
		return "ok";
	}

	@RequestMapping(value = "/calendar", method = RequestMethod.GET)
	public String showCalendar(Model model, HttpSession session)
	{
		SimpleDateFormat l_fmt = new SimpleDateFormat("dd/MM/yyyy");
		Date l_currentDate = new Date();
		try {			
			
			String l_json = getMonthHolidayJson(l_fmt.format(l_currentDate)) + "::" + getMonthLeavesJson(l_fmt.format(l_currentDate), ((User)session.getAttribute("user")).getLoginID());
			model.addAttribute("events", l_json);
			model.addAttribute("date", l_fmt.format(l_currentDate));
		} catch (Exception ex) {
			model.addAttribute("error", ex.getMessage());
			return "error";
		}
		return "calendar";
	}
	
	@RequestMapping(value = "/calendar", method = RequestMethod.POST)
	public String submitCalendar(Model model, @RequestParam String action, @RequestParam String date, HttpSession session)
	{
		try {
			SimpleDateFormat l_fmt = new SimpleDateFormat("dd/MM/yyyy");
			Date l_date = l_fmt.parse(date);
			Calendar l_cal = Calendar.getInstance();
			l_cal.setTime(l_date);
			if(action.equals("1")) // next
			{
				l_cal.add(Calendar.MONTH, 1);
			}
			else // previous
			{
				l_cal.add(Calendar.MONTH, -1);
			}
			
			l_date = l_cal.getTime();
			String l_json = getMonthHolidayJson(l_fmt.format(l_date)) + "::" + getMonthLeavesJson(l_fmt.format(l_date), ((User)session.getAttribute("user")).getLoginID());
			model.addAttribute("events", l_json);
			model.addAttribute("date", l_fmt.format(l_date));
		} catch (Exception ex) {
			model.addAttribute("error", ex.getMessage());
			return "error";
		}
		return "calendar";
	}
	
	@RequestMapping(value = "/leaves", method = RequestMethod.GET)
	public String leaveList(Model model, HttpSession session)
	{
		try {			
			String l_empID = ((User)session.getAttribute("user")).getLoginID();
			
			LeaveCri l_cri = new LeaveCri();
			l_cri.setRowIndex(0);
			l_cri.setRowLimit(5);
			l_cri.setEmployeID(l_empID);
			l_cri.setLeaveStatus((byte)0);
			
			List<Leave> l_LeaveList = leaveDAO.getLeaves(l_cri);
			model.addAttribute("leaveList", l_LeaveList);
			model.addAttribute("criteria", l_cri);
		}catch(Exception ex)
		{
			model.addAttribute("error", ex.getMessage());
			return "error";
		}
		return "leave_history";
	}
	
	@RequestMapping(value = "/leaves", method = RequestMethod.POST)
	public String rowLimitChange(Model model,WebRequest webRequest, HttpSession session)
	{
		try {
			String l_empID = ((User)session.getAttribute("user")).getLoginID();
			
			LeaveCri l_cri = new LeaveCri();
			l_cri.setRowIndex(0);
			l_cri.setKeyword(webRequest.getParameter("txtKeyword"));
			l_cri.setRowLimit(Integer.parseInt(webRequest.getParameter("cboRows")));
			l_cri.setEmployeID(l_empID);
			l_cri.setLeaveStatus((byte)0);
			
			List<Leave> l_LeaveList = leaveDAO.getLeaves(l_cri);
			model.addAttribute("leaveList", l_LeaveList);
			model.addAttribute("criteria", l_cri);
		}catch(Exception ex)
		{
			model.addAttribute("error", ex.getMessage());
			return "error";
		}
		return "leave_history";
	}

	@RequestMapping(value = "/leaves", method = RequestMethod.POST, params = "btnNext")
	public String next(Model model, WebRequest webRequest, HttpSession session) {
		try {
			String l_empID = ((User)session.getAttribute("user")).getLoginID();
			
			LeaveCri l_cri = new LeaveCri();
			l_cri.setKeyword(webRequest.getParameter("txtKeyword"));
			l_cri.setRowLimit(Integer.parseInt(webRequest.getParameter("cboRows")));
			l_cri.setRowIndex(Integer.parseInt(webRequest.getParameter("rowIndex")) + l_cri.getRowLimit());
			l_cri.setEmployeID(l_empID);
			l_cri.setLeaveStatus((byte)0);
			List<Leave> l_LeaveList =leaveDAO.getLeaves(l_cri);
			model.addAttribute("criteria", l_cri);
			model.addAttribute("leaveList", l_LeaveList);
		} catch (Exception ex) {
			model.addAttribute("error", ex.getMessage());
			return "error";
		}
		return "leave_history";
	}

	@RequestMapping(value = "/leaves", method = RequestMethod.POST, params = "btnPrev")
	public String prev(Model model, WebRequest webRequest, HttpSession session) {
		try {
			String l_empID = ((User)session.getAttribute("user")).getLoginID();
			LeaveCri l_cri = new LeaveCri();
			l_cri.setKeyword(webRequest.getParameter("txtKeyword"));
			l_cri.setRowLimit(Integer.parseInt(webRequest.getParameter("cboRows")));
			l_cri.setRowIndex(Integer.parseInt(webRequest.getParameter("rowIndex")) - l_cri.getRowLimit());
			l_cri.setLeaveStatus((byte)0);
			l_cri.setEmployeID(l_empID);
			List<Leave> l_LeaveList =leaveDAO.getLeaves(l_cri);
			model.addAttribute("criteria", l_cri);
			model.addAttribute("leaveList", l_LeaveList);
		} catch (Exception ex) {
			model.addAttribute("error", ex.getMessage());
			return "error";
		}
		return "leave_history";
	}

	private void setAvailableLeaves(Model model, String empID, Date date) throws SQLException
	{
		Employee l_emp = employeeDAO.getEmployee(empID);
		Leavesentitlement l_en = leavesentitlementDAO.getLeavesentitlement(l_emp.getLeaveEntitlementID());
		
		String l_date = LapsUtil.dateToString(date).substring(0, 4);
		int l_aLeave = l_en.getAnnualLeave() - leaveDAO.getLeavesCountByDate(l_date , empID, LeaveType.annual);
		int l_mLeave = l_en.getMedicalLeave() - leaveDAO.getLeavesCountByDate(l_date , empID, LeaveType.medical);
		int l_cLeave = l_en.getCompensationLeave() - leaveDAO.getLeavesCountByDate(l_date , empID, LeaveType.compensation);
		
		model.addAttribute("annualCount", l_aLeave);
		model.addAttribute("medicalCount", l_mLeave);
		model.addAttribute("compensationCount", l_cLeave);
	}
	
	@RequestMapping(value = "/leaves/new", method = RequestMethod.GET)
	public String newLeaves(Model model, WebRequest webRequest, HttpSession session) {
		
		try {
			String l_empID = ((User)session.getAttribute("user")).getLoginID();
			setAvailableLeaves(model, l_empID, new Date());
			
			Leave l_leave = new Leave();
			if(webRequest.getParameter("date") != null)
				l_leave.setStartDate(webRequest.getParameter("date"));
			model.addAttribute("leave", l_leave);
		} catch (Exception ex) {
			model.addAttribute("error", ex.getMessage());
			return "error";
		}
		return "leave";
	}
	
	@RequestMapping(value = "/leaves/new", method = RequestMethod.POST, params = "apply")
	public String applyLeave(Model model, @ModelAttribute("SpringWeb")Leave leave, HttpSession session)
	{
		try {
			String l_empID = ((User)session.getAttribute("user")).getLoginID();
			
			Employee l_emp = employeeDAO.getEmployee(l_empID);
			
			leave.setCreatedDate(LapsUtil.dateToString(new Date()));
			leave.setModifiedDate(leave.getCreatedDate());
			leave.setLeaveStatus(LeaveStatus.pending);
			leave.setEmployeeID(l_empID);
			leave.setStartDate(LapsUtil.formattedDateToString(leave.getStartDate()));
			leave.setEndDate(LapsUtil.formattedDateToString(leave.getEndDate()));
			leave.setSupervisorID(l_emp.getSupervisorID());
			leave.setDays(getDays(leave.getStartDate(), leave.getEndDate(), leave.getLeaveCategory()));
			
			String l_result = validateLeave(leave.getStartDate(), l_empID, model, leave.getLeaveCategory());
			if(!l_result.equals("ok"))
				return l_result;
			leaveDAO.addLeave(leave);
		} catch (Exception ex) {
			model.addAttribute("error", ex.getMessage());
			return "error";
		}
		return "redirect:/leaves";
	}
	
	@RequestMapping(value = "/leaves/{leaveID}", method = RequestMethod.GET)
	public String showLeave(Model model, HttpSession session, @PathVariable long leaveID)
	{
		try {
			String l_empID = ((User)session.getAttribute("user")).getLoginID();
			setAvailableLeaves(model, l_empID, new Date());
			
			Leave l_leave = leaveDAO.getLeave(leaveID);
			model.addAttribute("leave", l_leave);
		} catch (Exception ex) {
			model.addAttribute("error", ex.getMessage());
			return "error";
		}
		
		return "leave";
	}
	
	@RequestMapping(value = "/leaves/*", method = RequestMethod.POST, params = "edit")
	public String updateLeave(Model model, @ModelAttribute("SpringWeb")Leave leave, HttpSession session)
	{
		try {
			String l_empID = ((User)session.getAttribute("user")).getLoginID();
			Employee l_emp = employeeDAO.getEmployee(l_empID);
			
			leave.setSupervisorID(l_emp.getSupervisorID());
			leave.setModifiedDate(LapsUtil.dateToString(new Date()));
			leave.setStartDate(LapsUtil.formattedDateToString(leave.getStartDate()));
			leave.setEndDate(LapsUtil.formattedDateToString(leave.getEndDate()));
			leave.setDays(getDays(leave.getStartDate(), leave.getEndDate(), leave.getLeaveCategory()));
			
			String l_result = validateLeave(leave.getStartDate(), l_empID, model, leave.getLeaveCategory());
			if(!l_result.equals("ok"))
				return l_result;
			leaveDAO.updateLeave(leave);
		} catch (Exception ex) {
			model.addAttribute("error", ex.getMessage());
			return "error";
		}
		return "redirect:/leaves";
	}
	
	@RequestMapping(value = "/leaves/*", method = RequestMethod.POST, params = "delete")
	public String deleteLeave(Model model, @RequestParam long leaveID)
	{
		try {
			leaveDAO.deleteLeave(leaveID);
		} catch (Exception ex) {
			model.addAttribute("error", ex.getMessage());
			return "error";
		}
		return "redirect:/leaves";
	}
	
	@RequestMapping(value = "/leaves/*", method = RequestMethod.POST, params = "cancel")
	public String cancelLeave(Model model, @RequestParam long leaveID)
	{
		try {
			leaveDAO.updateLeaveStatus(leaveID, LeaveStatus.cancel);
		} catch (Exception ex) {
			model.addAttribute("error", ex.getMessage());
			return "error";
		}
		return "redirect:/leaves";
	}
	
	@RequestMapping(value = "/leaves/applied", method = RequestMethod.GET)
	public String showAppliedLeave(Model model, HttpSession session) {
		
		try {
			String l_empID = ((User)session.getAttribute("user")).getLoginID();
			
			LeaveCri l_cri = new LeaveCri();
			l_cri.setRowIndex(0);
			l_cri.setRowLimit(10);
			l_cri.setSupervisorID(l_empID);
			l_cri.setLeaveStatus(LeaveStatus.pending);
			
			List<Leave> l_LeaveList = leaveDAO.getLeaves(l_cri);
			model.addAttribute("leaveList", l_LeaveList);
			model.addAttribute("criteria", l_cri);
		}catch(Exception ex)
		{
			model.addAttribute("error", ex.getMessage());
			return "error";
		}
		return "show_applied_leave";
	}

	@RequestMapping(value = "/leaves/applied", method = RequestMethod.POST)
	public String statusChanged(Model model,  WebRequest webRequest, HttpSession session)
	{
		try {
			String l_empID = ((User)session.getAttribute("user")).getLoginID();
			
			LeaveCri l_cri = new LeaveCri();
			l_cri.setRowIndex(0);
			l_cri.setLeaveStatus(Byte.parseByte(webRequest.getParameter("leaveStatus")));
			l_cri.setRowLimit(Integer.parseInt(webRequest.getParameter("cboRows")));
			l_cri.setSupervisorID(l_empID);
			
			List<Leave> l_LeaveList = leaveDAO.getLeaves(l_cri);
			model.addAttribute("leaveList", l_LeaveList);
			model.addAttribute("criteria", l_cri);
		}catch(Exception ex)
		{
			model.addAttribute("error", ex.getMessage());
			return "error";
		}
		return "show_applied_leave";
	}
	
	@RequestMapping(value = "/leaves/applied/{leaveid}", method = RequestMethod.GET)
	public String showApproval(Locale locale, Model model,
			@PathVariable long leaveid) {
		try {
			Leave l_leave = leaveDAO.getLeave(leaveid);
			model.addAttribute("emp_Name", employeeDAO.getEmployee(l_leave.getEmployeeID()));
			model.addAttribute("leave", l_leave);
		} catch (Exception ex) {
			model.addAttribute("error", ex.getMessage());
			return "error";
		}
		return "approval";
	}
	
	@RequestMapping(value = "/leaves/applied/*", method = RequestMethod.POST)
	public String confirmLeave(Model model, @RequestParam long leaveID, @RequestParam String confirmation, @RequestParam String comment)
	{
		try {
			if(confirmation.equals("approve"))
			{
				leaveDAO.updateLeaveStatus(leaveID, LeaveStatus.approve, comment);
			}
			else
			{
				leaveDAO.updateLeaveStatus(leaveID, LeaveStatus.reject, comment);
			}
		} catch (Exception ex) {
			model.addAttribute("error", ex.getMessage());
			return "error";
		}
		
		
		return "redirect:/leaves/applied";
	}
}
